Enter the directory of the maca folder on your drive and the name of the tissue you want to analyze.

tissue_of_interest = "Spleen"

Load the requisite packages and some additional helper functions.

library(here)
library(useful)
library(Seurat)
library(dplyr)
library(Matrix)
library(ontologyIndex)
cell_ontology = get_ontology('https://raw.githubusercontent.com/obophenotype/cell-ontology/master/cl-basic.obo', extract_tags='everything')
validate_cell_ontology = function(cell_ontology_class){
  in_cell_ontology = sapply(cell_ontology_class, function(x) is.element(x, cell_ontology$name) || is.na(x))
  if (!all(in_cell_ontology)) {
    message = paste0('"', cell_ontology_class[!in_cell_ontology], '" is not in the cell ontology
')
    stop(message)
  }
}
convert_to_cell_ontology_id = function(cell_ontology_class){
  return(sapply(cell_ontology_class, function(x) as.vector(cell_ontology$id[cell_ontology$name == x])[1]))
}
save_dir = here('00_data_ingest', 'tissue_robj')
# read the metadata to get the plates we want
plate_metadata_filename = here('00_data_ingest', '00_facs_raw_data', 'metadata_FACS.csv')
plate_metadata <- read.csv(plate_metadata_filename, sep=",", header = TRUE)
colnames(plate_metadata)[1] <- "plate.barcode"
plate_metadata

Subset the metadata on the tissue.

tissue_plates = filter(plate_metadata, tissue == tissue_of_interest)[,c('plate.barcode','tissue','subtissue','mouse.sex')]
tissue_plates

Load the read count data.

#Load the gene names and set the metadata columns by opening the first file
filename = here('00_data_ingest', '00_facs_raw_data', 'FACS', paste0(tissue_of_interest, '-counts.csv'))
raw.data = read.csv(filename, sep=",", row.names=1)
# raw.data = data.frame(row.names = rownames(raw.data))
corner(raw.data)

Make a vector of plate barcodes for each cell

plate.barcodes = lapply(colnames(raw.data), function(x) strsplit(strsplit(x, "_")[[1]][1], '.', fixed=TRUE)[[1]][2])
head(plate.barcodes)
[[1]]
[1] "MAA000508"

[[2]]
[1] "MAA000508"

[[3]]
[1] "MAA000508"

[[4]]
[1] "MAA000508"

[[5]]
[1] "MAA000508"

[[6]]
[1] "MAA000508"

Use only the metadata rows corresponding to Bladder plates. Make a plate barcode dataframe to “expand” the per-plate metadata to be per-cell.

barcode.df = t.data.frame(as.data.frame(plate.barcodes))
rownames(barcode.df) = colnames(raw.data)
colnames(barcode.df) = c('plate.barcode')
head(barcode.df)
                        plate.barcode
A21.MAA000508.3_9_M.1.1 "MAA000508"  
C6.MAA000508.3_9_M.1.1  "MAA000508"  
A22.MAA000508.3_9_M.1.1 "MAA000508"  
C8.MAA000508.3_9_M.1.1  "MAA000508"  
E8.MAA000508.3_9_M.1.1  "MAA000508"  
E6.MAA000508.3_9_M.1.1  "MAA000508"  
rnames = row.names(barcode.df)
meta.data <- merge(barcode.df, plate_metadata, by='plate.barcode', sort = F)
row.names(meta.data) <- rnames
# Sort cells by plate barcode because that's how the data was originally
meta.data = meta.data[order(meta.data$plate.barcode), ]
corner(meta.data)
raw.data = raw.data[, rownames(meta.data)]
corner(raw.data)

Process the raw data and load it into the Seurat object.

# Find ERCC's, compute the percent ERCC, and drop them from the raw data.
erccs <- grep(pattern = "^ERCC-", x = rownames(x = raw.data), value = TRUE)
percent.ercc <- Matrix::colSums(raw.data[erccs, ])/Matrix::colSums(raw.data)
ercc.index <- grep(pattern = "^ERCC-", x = rownames(x = raw.data), value = FALSE)
raw.data <- raw.data[-ercc.index,]
# Create the Seurat object with all the data
tiss <- CreateSeuratObject(raw.data = raw.data, project = tissue_of_interest, 
                    min.cells = 5, min.genes = 5)
tiss <- AddMetaData(object = tiss, meta.data)
tiss <- AddMetaData(object = tiss, percent.ercc, col.name = "percent.ercc")
# Change default name for sums of counts from nUMI to nReads
colnames(tiss@meta.data)[colnames(tiss@meta.data) == 'nUMI'] <- 'nReads'
# Create metadata columns for cell_ontology_classs and subcell_ontology_classs
tiss@meta.data[,'free_annotation'] <- NA
tiss@meta.data[,'cell_ontology_class'] <- NA
tiss@meta.data[,'subcell_ontology_class'] <- NA

Calculate percent ribosomal genes.

ribo.genes <- grep(pattern = "^Rp[sl][[:digit:]]", x = rownames(x = tiss@data), value = TRUE)
percent.ribo <- Matrix::colSums(tiss@raw.data[ribo.genes, ])/Matrix::colSums(tiss@raw.data)
tiss <- AddMetaData(object = tiss, metadata = percent.ribo, col.name = "percent.ribo")

A sanity check: genes per cell vs reads per cell.

GenePlot(object = tiss, gene1 = "nReads", gene2 = "nGene", use.raw=T)

Filter out cells with few reads and few genes.

tiss <- FilterCells(object = tiss, subset.names = c("nGene", "nReads"), 
    low.thresholds = c(500, 50000), high.thresholds = c(25000, 2000000))

Normalize the data, then regress out correlation with total reads

tiss <- NormalizeData(object = tiss, scale.factor = 1e6)
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
tiss <- ScaleData(object = tiss)
[1] "Scaling data matrix"

  |                                                                                                                                 
  |                                                                                                                           |   0%
  |                                                                                                                                 
  |===========================================================================================================================| 100%
tiss <- FindVariableGenes(object = tiss, do.plot = TRUE, x.low.cutoff = 0.7 , x.high.cutoff = Inf, y.cutoff = 0.4)
Calculating gene means
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variance to mean ratios
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|

Run Principal Component Analysis.

tiss <- RunPCA(object = tiss, do.print = FALSE)
tiss <- ProjectPCA(object = tiss, do.print = FALSE)

Later on (in FindClusters and TSNE) you will pick a number of principal components to use. This has the effect of keeping the major directions of variation in the data and, ideally, supressing noise. There is no correct answer to the number to use, but a decent rule of thumb is to go until the plot plateaus.

PCElbowPlot(object = tiss)

Choose the number of principal components to use.

# Set number of principal components. 
n.pcs = 7

The clustering is performed based on a nearest neighbors graph. Cells that have similar expression will be joined together. The Louvain algorithm looks for groups of cells with high modularity–more connections within the group than between groups. The resolution parameter determines the scale…higher resolution will give more clusters, lower resolution will give fewer.

For the top-level clustering, aim to under-cluster instead of over-cluster. It will be easy to subset groups and further analyze them below.

# Set resolution 
res.used <- 2.5
tiss <- FindClusters(object = tiss, reduction.type = "pca", dims.use = 1:n.pcs, 
    resolution = res.used, print.output = 0, save.SNN = TRUE)

To visualize

# If cells are too spread out, you can raise the perplexity. If you have few cells, try a lower perplexity (but never less than 10).
tiss <- RunTSNE(object = tiss, dims.use = 1:n.pcs, seed.use = 10, perplexity=20)
# note that you can set do.label=T to help label individual clusters
TSNEPlot(object = tiss, do.label = T)

Check expression of genes of interset.

Dotplots let you see the intensity of exppression and the fraction of cells expressing for each of your genes of interest.

How big are the clusters?

table(tiss@ident)

  0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15 
147 144 141 136 135 129 128 125 117 113 104  77  58  50  49  36 

Which markers identify a specific cluster?

clust.markers <- FindMarkers(object = tiss, ident.1 = 3, ident.2 = 1, only.pos = TRUE, min.pct = 0.25, thresh.use = 0.25)

   |                                                  | 0 % ~calculating  
   |+                                                 | 1 % ~09s          
   |++                                                | 2 % ~08s          
   |++                                                | 3 % ~08s          
   |+++                                               | 4 % ~08s          
   |+++                                               | 5 % ~08s          
   |++++                                              | 6 % ~08s          
   |++++                                              | 7 % ~08s          
   |+++++                                             | 8 % ~08s          
   |+++++                                             | 9 % ~08s          
   |++++++                                            | 10% ~08s          
   |++++++                                            | 11% ~08s          
   |+++++++                                           | 12% ~07s          
   |+++++++                                           | 13% ~07s          
   |++++++++                                          | 14% ~07s          
   |++++++++                                          | 15% ~07s          
   |+++++++++                                         | 16% ~07s          
   |+++++++++                                         | 17% ~07s          
   |++++++++++                                        | 18% ~07s          
   |++++++++++                                        | 19% ~07s          
   |+++++++++++                                       | 20% ~07s          
   |+++++++++++                                       | 21% ~07s          
   |++++++++++++                                      | 22% ~07s          
   |++++++++++++                                      | 23% ~07s          
   |+++++++++++++                                     | 24% ~06s          
   |+++++++++++++                                     | 26% ~06s          
   |++++++++++++++                                    | 27% ~06s          
   |++++++++++++++                                    | 28% ~06s          
   |+++++++++++++++                                   | 29% ~06s          
   |+++++++++++++++                                   | 30% ~06s          
   |++++++++++++++++                                  | 31% ~06s          
   |++++++++++++++++                                  | 32% ~06s          
   |+++++++++++++++++                                 | 33% ~05s          
   |+++++++++++++++++                                 | 34% ~05s          
   |++++++++++++++++++                                | 35% ~05s          
   |++++++++++++++++++                                | 36% ~05s          
   |+++++++++++++++++++                               | 37% ~05s          
   |+++++++++++++++++++                               | 38% ~05s          
   |++++++++++++++++++++                              | 39% ~05s          
   |++++++++++++++++++++                              | 40% ~05s          
   |+++++++++++++++++++++                             | 41% ~05s          
   |+++++++++++++++++++++                             | 42% ~05s          
   |++++++++++++++++++++++                            | 43% ~04s          
   |++++++++++++++++++++++                            | 44% ~04s          
   |+++++++++++++++++++++++                           | 45% ~04s          
   |+++++++++++++++++++++++                           | 46% ~04s          
   |++++++++++++++++++++++++                          | 47% ~04s          
   |++++++++++++++++++++++++                          | 48% ~04s          
   |+++++++++++++++++++++++++                         | 49% ~04s          
   |+++++++++++++++++++++++++                         | 50% ~04s          
   |++++++++++++++++++++++++++                        | 51% ~04s          
   |+++++++++++++++++++++++++++                       | 52% ~04s          
   |+++++++++++++++++++++++++++                       | 53% ~04s          
   |++++++++++++++++++++++++++++                      | 54% ~03s          
   |++++++++++++++++++++++++++++                      | 55% ~03s          
   |+++++++++++++++++++++++++++++                     | 56% ~03s          
   |+++++++++++++++++++++++++++++                     | 57% ~03s          
   |++++++++++++++++++++++++++++++                    | 58% ~03s          
   |++++++++++++++++++++++++++++++                    | 59% ~03s          
   |+++++++++++++++++++++++++++++++                   | 60% ~03s          
   |+++++++++++++++++++++++++++++++                   | 61% ~03s          
   |++++++++++++++++++++++++++++++++                  | 62% ~03s          
   |++++++++++++++++++++++++++++++++                  | 63% ~03s          
   |+++++++++++++++++++++++++++++++++                 | 64% ~03s          
   |+++++++++++++++++++++++++++++++++                 | 65% ~03s          
   |++++++++++++++++++++++++++++++++++                | 66% ~03s          
   |++++++++++++++++++++++++++++++++++                | 67% ~02s          
   |+++++++++++++++++++++++++++++++++++               | 68% ~02s          
   |+++++++++++++++++++++++++++++++++++               | 69% ~02s          
   |++++++++++++++++++++++++++++++++++++              | 70% ~02s          
   |++++++++++++++++++++++++++++++++++++              | 71% ~02s          
   |+++++++++++++++++++++++++++++++++++++             | 72% ~02s          
   |+++++++++++++++++++++++++++++++++++++             | 73% ~02s          
   |++++++++++++++++++++++++++++++++++++++            | 74% ~02s          
   |++++++++++++++++++++++++++++++++++++++            | 76% ~02s          
   |+++++++++++++++++++++++++++++++++++++++           | 77% ~02s          
   |+++++++++++++++++++++++++++++++++++++++           | 78% ~02s          
   |++++++++++++++++++++++++++++++++++++++++          | 79% ~02s          
   |++++++++++++++++++++++++++++++++++++++++          | 80% ~02s          
   |+++++++++++++++++++++++++++++++++++++++++         | 81% ~01s          
   |+++++++++++++++++++++++++++++++++++++++++         | 82% ~01s          
   |++++++++++++++++++++++++++++++++++++++++++        | 83% ~01s          
   |++++++++++++++++++++++++++++++++++++++++++        | 84% ~01s          
   |+++++++++++++++++++++++++++++++++++++++++++       | 85% ~01s          
   |+++++++++++++++++++++++++++++++++++++++++++       | 86% ~01s          
   |++++++++++++++++++++++++++++++++++++++++++++      | 87% ~01s          
   |++++++++++++++++++++++++++++++++++++++++++++      | 88% ~01s          
   |+++++++++++++++++++++++++++++++++++++++++++++     | 89% ~01s          
   |+++++++++++++++++++++++++++++++++++++++++++++     | 90% ~01s          
   |++++++++++++++++++++++++++++++++++++++++++++++    | 91% ~01s          
   |++++++++++++++++++++++++++++++++++++++++++++++    | 92% ~01s          
   |+++++++++++++++++++++++++++++++++++++++++++++++   | 93% ~01s          
   |+++++++++++++++++++++++++++++++++++++++++++++++   | 94% ~00s          
   |++++++++++++++++++++++++++++++++++++++++++++++++  | 95% ~00s          
   |++++++++++++++++++++++++++++++++++++++++++++++++  | 96% ~00s          
   |+++++++++++++++++++++++++++++++++++++++++++++++++ | 97% ~00s          
   |+++++++++++++++++++++++++++++++++++++++++++++++++ | 98% ~00s          
   |++++++++++++++++++++++++++++++++++++++++++++++++++| 99% ~00s          
   |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed = 07s
print(x = head(x= clust.markers, n = 20, avg_diff))

You can also compute all markers for all clusters at once. This may take some time.

tiss.markers <- FindAllMarkers(object = tiss, only.pos = TRUE, min.pct = 0.25, thresh.use = 0.25)

   |                                                  | 0 % ~calculating  
   |+                                                 | 1 % ~01m 20s      
   |++                                                | 2 % ~01m 17s      
   |++                                                | 3 % ~01m 15s      
   |+++                                               | 4 % ~01m 15s      
   |+++                                               | 5 % ~01m 14s      
   |++++                                              | 6 % ~01m 14s      
   |++++                                              | 7 % ~01m 13s      
   |+++++                                             | 8 % ~01m 14s      
   |+++++                                             | 9 % ~01m 13s      
   |++++++                                            | 10% ~01m 12s      
   |++++++                                            | 11% ~01m 11s      
   |+++++++                                           | 12% ~01m 10s      
   |+++++++                                           | 13% ~01m 09s      
   |++++++++                                          | 14% ~01m 09s      
   |++++++++                                          | 15% ~01m 08s      
   |+++++++++                                         | 16% ~01m 08s      
   |+++++++++                                         | 17% ~01m 07s      
   |++++++++++                                        | 18% ~01m 06s      
   |++++++++++                                        | 19% ~01m 05s      
   |+++++++++++                                       | 20% ~01m 04s      
   |+++++++++++                                       | 21% ~01m 03s      
   |++++++++++++                                      | 22% ~01m 02s      
   |++++++++++++                                      | 23% ~01m 01s      
   |+++++++++++++                                     | 24% ~01m 01s      
   |+++++++++++++                                     | 26% ~01m 00s      
   |++++++++++++++                                    | 27% ~60s          
   |++++++++++++++                                    | 28% ~59s          
   |+++++++++++++++                                   | 29% ~58s          
   |+++++++++++++++                                   | 30% ~57s          
   |++++++++++++++++                                  | 31% ~56s          
   |++++++++++++++++                                  | 32% ~55s          
   |+++++++++++++++++                                 | 33% ~54s          
   |+++++++++++++++++                                 | 34% ~53s          
   |++++++++++++++++++                                | 35% ~52s          
   |++++++++++++++++++                                | 36% ~51s          
   |+++++++++++++++++++                               | 37% ~50s          
   |+++++++++++++++++++                               | 38% ~50s          
   |++++++++++++++++++++                              | 39% ~49s          
   |++++++++++++++++++++                              | 40% ~48s          
   |+++++++++++++++++++++                             | 41% ~47s          
   |+++++++++++++++++++++                             | 42% ~47s          
   |++++++++++++++++++++++                            | 43% ~46s          
   |++++++++++++++++++++++                            | 44% ~45s          
   |+++++++++++++++++++++++                           | 45% ~44s          
   |+++++++++++++++++++++++                           | 46% ~44s          
   |++++++++++++++++++++++++                          | 47% ~43s          
   |++++++++++++++++++++++++                          | 48% ~42s          
   |+++++++++++++++++++++++++                         | 49% ~41s          
   |+++++++++++++++++++++++++                         | 50% ~41s          
   |++++++++++++++++++++++++++                        | 51% ~40s          
   |+++++++++++++++++++++++++++                       | 52% ~39s          
   |+++++++++++++++++++++++++++                       | 53% ~38s          
   |++++++++++++++++++++++++++++                      | 54% ~37s          
   |++++++++++++++++++++++++++++                      | 55% ~37s          
   |+++++++++++++++++++++++++++++                     | 56% ~36s          
   |+++++++++++++++++++++++++++++                     | 57% ~35s          
   |++++++++++++++++++++++++++++++                    | 58% ~34s          
   |++++++++++++++++++++++++++++++                    | 59% ~33s          
   |+++++++++++++++++++++++++++++++                   | 60% ~33s          
   |+++++++++++++++++++++++++++++++                   | 61% ~32s          
   |++++++++++++++++++++++++++++++++                  | 62% ~31s          
   |++++++++++++++++++++++++++++++++                  | 63% ~30s          
   |+++++++++++++++++++++++++++++++++                 | 64% ~29s          
   |+++++++++++++++++++++++++++++++++                 | 65% ~28s          
   |++++++++++++++++++++++++++++++++++                | 66% ~28s          
   |++++++++++++++++++++++++++++++++++                | 67% ~27s          
   |+++++++++++++++++++++++++++++++++++               | 68% ~26s          
   |+++++++++++++++++++++++++++++++++++               | 69% ~25s          
   |++++++++++++++++++++++++++++++++++++              | 70% ~24s          
   |++++++++++++++++++++++++++++++++++++              | 71% ~23s          
   |+++++++++++++++++++++++++++++++++++++             | 72% ~22s          
   |+++++++++++++++++++++++++++++++++++++             | 73% ~22s          
   |++++++++++++++++++++++++++++++++++++++            | 74% ~21s          
   |++++++++++++++++++++++++++++++++++++++            | 76% ~20s          
   |+++++++++++++++++++++++++++++++++++++++           | 77% ~19s          
   |+++++++++++++++++++++++++++++++++++++++           | 78% ~18s          
   |++++++++++++++++++++++++++++++++++++++++          | 79% ~17s          
   |++++++++++++++++++++++++++++++++++++++++          | 80% ~17s          
   |+++++++++++++++++++++++++++++++++++++++++         | 81% ~16s          
   |+++++++++++++++++++++++++++++++++++++++++         | 82% ~15s          
   |++++++++++++++++++++++++++++++++++++++++++        | 83% ~14s          
   |++++++++++++++++++++++++++++++++++++++++++        | 84% ~13s          
   |+++++++++++++++++++++++++++++++++++++++++++       | 85% ~12s          
   |+++++++++++++++++++++++++++++++++++++++++++       | 86% ~12s          
   |++++++++++++++++++++++++++++++++++++++++++++      | 87% ~11s          
   |++++++++++++++++++++++++++++++++++++++++++++      | 88% ~10s          
   |+++++++++++++++++++++++++++++++++++++++++++++     | 89% ~09s          
   |+++++++++++++++++++++++++++++++++++++++++++++     | 90% ~08s          
   |++++++++++++++++++++++++++++++++++++++++++++++    | 91% ~07s          
   |++++++++++++++++++++++++++++++++++++++++++++++    | 92% ~07s          
   |+++++++++++++++++++++++++++++++++++++++++++++++   | 93% ~06s          
   |+++++++++++++++++++++++++++++++++++++++++++++++   | 94% ~05s          
   |++++++++++++++++++++++++++++++++++++++++++++++++  | 95% ~04s          
   |++++++++++++++++++++++++++++++++++++++++++++++++  | 96% ~03s          
   |+++++++++++++++++++++++++++++++++++++++++++++++++ | 97% ~02s          
   |+++++++++++++++++++++++++++++++++++++++++++++++++ | 98% ~02s          
   |++++++++++++++++++++++++++++++++++++++++++++++++++| 99% ~01s          
   |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed = 01m 21s

   |                                                  | 0 % ~calculating  
   |+                                                 | 1 % ~33s          
   |++                                                | 2 % ~31s          
   |++                                                | 3 % ~30s          
   |+++                                               | 4 % ~29s          
   |+++                                               | 5 % ~28s          
   |++++                                              | 6 % ~28s          
   |++++                                              | 7 % ~28s          
   |+++++                                             | 8 % ~28s          
   |+++++                                             | 9 % ~28s          
   |++++++                                            | 10% ~28s          
   |++++++                                            | 11% ~29s          
   |+++++++                                           | 12% ~31s          
   |+++++++                                           | 13% ~30s          
   |++++++++                                          | 14% ~30s          
   |++++++++                                          | 15% ~30s          
   |+++++++++                                         | 16% ~29s          
   |+++++++++                                         | 17% ~29s          

Display the top markers you computed above.

Assigning cell type identity to clusters

At a coarse level, we can use canonical markers to match the unbiased clustering to known cell types:

Checking for batch effects

Color by metadata, like plate barcode, to check for batch effects.

Print a table showing the count of cells in each identity category from each plate.

Save the Robject for later

When you save the annotated tissue, please give it a name.

Export the final metadata

So that Biohub can easily combine all your cell_ontology_classs, please export them as a simple csv.

LS0tCnRpdGxlOiAiU3BsZWVuIEZBQ1MgTm90ZWJvb2siCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCkVudGVyIHRoZSBkaXJlY3Rvcnkgb2YgdGhlIG1hY2EgZm9sZGVyIG9uIHlvdXIgZHJpdmUgYW5kIHRoZSBuYW1lIG9mIHRoZSB0aXNzdWUgeW91IHdhbnQgdG8gYW5hbHl6ZS4KCmBgYHtyfQp0aXNzdWVfb2ZfaW50ZXJlc3QgPSAiU3BsZWVuIgpgYGAKCkxvYWQgdGhlIHJlcXVpc2l0ZSBwYWNrYWdlcyBhbmQgc29tZSBhZGRpdGlvbmFsIGhlbHBlciBmdW5jdGlvbnMuCgpgYGB7cn0KbGlicmFyeShoZXJlKQpsaWJyYXJ5KHVzZWZ1bCkKbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoTWF0cml4KQpsaWJyYXJ5KG9udG9sb2d5SW5kZXgpCmNlbGxfb250b2xvZ3kgPSBnZXRfb250b2xvZ3koJ2h0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9vYm9waGVub3R5cGUvY2VsbC1vbnRvbG9neS9tYXN0ZXIvY2wtYmFzaWMub2JvJywgZXh0cmFjdF90YWdzPSdldmVyeXRoaW5nJykKCnZhbGlkYXRlX2NlbGxfb250b2xvZ3kgPSBmdW5jdGlvbihjZWxsX29udG9sb2d5X2NsYXNzKXsKICBpbl9jZWxsX29udG9sb2d5ID0gc2FwcGx5KGNlbGxfb250b2xvZ3lfY2xhc3MsIGZ1bmN0aW9uKHgpIGlzLmVsZW1lbnQoeCwgY2VsbF9vbnRvbG9neSRuYW1lKSB8fCBpcy5uYSh4KSkKICBpZiAoIWFsbChpbl9jZWxsX29udG9sb2d5KSkgewogICAgbWVzc2FnZSA9IHBhc3RlMCgnIicsIGNlbGxfb250b2xvZ3lfY2xhc3NbIWluX2NlbGxfb250b2xvZ3ldLCAnIiBpcyBub3QgaW4gdGhlIGNlbGwgb250b2xvZ3kKJykKICAgIHN0b3AobWVzc2FnZSkKICB9Cn0KY29udmVydF90b19jZWxsX29udG9sb2d5X2lkID0gZnVuY3Rpb24oY2VsbF9vbnRvbG9neV9jbGFzcyl7CiAgcmV0dXJuKHNhcHBseShjZWxsX29udG9sb2d5X2NsYXNzLCBmdW5jdGlvbih4KSBhcy52ZWN0b3IoY2VsbF9vbnRvbG9neSRpZFtjZWxsX29udG9sb2d5JG5hbWUgPT0geF0pWzFdKSkKfQoKc2F2ZV9kaXIgPSBoZXJlKCcwMF9kYXRhX2luZ2VzdCcsICd0aXNzdWVfcm9iaicpCmBgYAoKCgpgYGB7cn0KIyByZWFkIHRoZSBtZXRhZGF0YSB0byBnZXQgdGhlIHBsYXRlcyB3ZSB3YW50CnBsYXRlX21ldGFkYXRhX2ZpbGVuYW1lID0gaGVyZSgnMDBfZGF0YV9pbmdlc3QnLCAnMDBfZmFjc19yYXdfZGF0YScsICdtZXRhZGF0YV9GQUNTLmNzdicpCgpwbGF0ZV9tZXRhZGF0YSA8LSByZWFkLmNzdihwbGF0ZV9tZXRhZGF0YV9maWxlbmFtZSwgc2VwPSIsIiwgaGVhZGVyID0gVFJVRSkKY29sbmFtZXMocGxhdGVfbWV0YWRhdGEpWzFdIDwtICJwbGF0ZS5iYXJjb2RlIgpwbGF0ZV9tZXRhZGF0YQpgYGAKClN1YnNldCB0aGUgbWV0YWRhdGEgb24gdGhlIHRpc3N1ZS4KCmBgYHtyfQp0aXNzdWVfcGxhdGVzID0gZmlsdGVyKHBsYXRlX21ldGFkYXRhLCB0aXNzdWUgPT0gdGlzc3VlX29mX2ludGVyZXN0KVssYygncGxhdGUuYmFyY29kZScsJ3Rpc3N1ZScsJ3N1YnRpc3N1ZScsJ21vdXNlLnNleCcpXQp0aXNzdWVfcGxhdGVzCmBgYAoKTG9hZCB0aGUgcmVhZCBjb3VudCBkYXRhLgpgYGB7cn0KI0xvYWQgdGhlIGdlbmUgbmFtZXMgYW5kIHNldCB0aGUgbWV0YWRhdGEgY29sdW1ucyBieSBvcGVuaW5nIHRoZSBmaXJzdCBmaWxlCmZpbGVuYW1lID0gaGVyZSgnMDBfZGF0YV9pbmdlc3QnLCAnMDBfZmFjc19yYXdfZGF0YScsICdGQUNTJywgcGFzdGUwKHRpc3N1ZV9vZl9pbnRlcmVzdCwgJy1jb3VudHMuY3N2JykpCgpyYXcuZGF0YSA9IHJlYWQuY3N2KGZpbGVuYW1lLCBzZXA9IiwiLCByb3cubmFtZXM9MSkKIyByYXcuZGF0YSA9IGRhdGEuZnJhbWUocm93Lm5hbWVzID0gcm93bmFtZXMocmF3LmRhdGEpKQpjb3JuZXIocmF3LmRhdGEpCmBgYApNYWtlIGEgdmVjdG9yIG9mIHBsYXRlIGJhcmNvZGVzIGZvciBlYWNoIGNlbGwKCmBgYHtyfQpwbGF0ZS5iYXJjb2RlcyA9IGxhcHBseShjb2xuYW1lcyhyYXcuZGF0YSksIGZ1bmN0aW9uKHgpIHN0cnNwbGl0KHN0cnNwbGl0KHgsICJfIilbWzFdXVsxXSwgJy4nLCBmaXhlZD1UUlVFKVtbMV1dWzJdKQpoZWFkKHBsYXRlLmJhcmNvZGVzKQpgYGAKClVzZSBvbmx5IHRoZSBtZXRhZGF0YSByb3dzIGNvcnJlc3BvbmRpbmcgdG8gQmxhZGRlciBwbGF0ZXMuIE1ha2UgYSBwbGF0ZSBiYXJjb2RlIGRhdGFmcmFtZSB0byAiZXhwYW5kIiB0aGUgcGVyLXBsYXRlIG1ldGFkYXRhIHRvIGJlIHBlci1jZWxsLgpgYGB7cn0KYmFyY29kZS5kZiA9IHQuZGF0YS5mcmFtZShhcy5kYXRhLmZyYW1lKHBsYXRlLmJhcmNvZGVzKSkKCnJvd25hbWVzKGJhcmNvZGUuZGYpID0gY29sbmFtZXMocmF3LmRhdGEpCmNvbG5hbWVzKGJhcmNvZGUuZGYpID0gYygncGxhdGUuYmFyY29kZScpCmhlYWQoYmFyY29kZS5kZikKCnJuYW1lcyA9IHJvdy5uYW1lcyhiYXJjb2RlLmRmKQptZXRhLmRhdGEgPC0gbWVyZ2UoYmFyY29kZS5kZiwgcGxhdGVfbWV0YWRhdGEsIGJ5PSdwbGF0ZS5iYXJjb2RlJywgc29ydCA9IEYpCnJvdy5uYW1lcyhtZXRhLmRhdGEpIDwtIHJuYW1lcwoKIyBTb3J0IGNlbGxzIGJ5IHBsYXRlIGJhcmNvZGUgYmVjYXVzZSB0aGF0J3MgaG93IHRoZSBkYXRhIHdhcyBvcmlnaW5hbGx5Cm1ldGEuZGF0YSA9IG1ldGEuZGF0YVtvcmRlcihtZXRhLmRhdGEkcGxhdGUuYmFyY29kZSksIF0KY29ybmVyKG1ldGEuZGF0YSkKcmF3LmRhdGEgPSByYXcuZGF0YVssIHJvd25hbWVzKG1ldGEuZGF0YSldCmNvcm5lcihyYXcuZGF0YSkKYGBgClByb2Nlc3MgdGhlIHJhdyBkYXRhIGFuZCBsb2FkIGl0IGludG8gdGhlIFNldXJhdCBvYmplY3QuCgpgYGB7cn0KIyBGaW5kIEVSQ0MncywgY29tcHV0ZSB0aGUgcGVyY2VudCBFUkNDLCBhbmQgZHJvcCB0aGVtIGZyb20gdGhlIHJhdyBkYXRhLgplcmNjcyA8LSBncmVwKHBhdHRlcm4gPSAiXkVSQ0MtIiwgeCA9IHJvd25hbWVzKHggPSByYXcuZGF0YSksIHZhbHVlID0gVFJVRSkKcGVyY2VudC5lcmNjIDwtIE1hdHJpeDo6Y29sU3VtcyhyYXcuZGF0YVtlcmNjcywgXSkvTWF0cml4Ojpjb2xTdW1zKHJhdy5kYXRhKQplcmNjLmluZGV4IDwtIGdyZXAocGF0dGVybiA9ICJeRVJDQy0iLCB4ID0gcm93bmFtZXMoeCA9IHJhdy5kYXRhKSwgdmFsdWUgPSBGQUxTRSkKcmF3LmRhdGEgPC0gcmF3LmRhdGFbLWVyY2MuaW5kZXgsXQoKIyBDcmVhdGUgdGhlIFNldXJhdCBvYmplY3Qgd2l0aCBhbGwgdGhlIGRhdGEKdGlzcyA8LSBDcmVhdGVTZXVyYXRPYmplY3QocmF3LmRhdGEgPSByYXcuZGF0YSwgcHJvamVjdCA9IHRpc3N1ZV9vZl9pbnRlcmVzdCwgCiAgICAgICAgICAgICAgICAgICAgbWluLmNlbGxzID0gNSwgbWluLmdlbmVzID0gNSkKCnRpc3MgPC0gQWRkTWV0YURhdGEob2JqZWN0ID0gdGlzcywgbWV0YS5kYXRhKQp0aXNzIDwtIEFkZE1ldGFEYXRhKG9iamVjdCA9IHRpc3MsIHBlcmNlbnQuZXJjYywgY29sLm5hbWUgPSAicGVyY2VudC5lcmNjIikKIyBDaGFuZ2UgZGVmYXVsdCBuYW1lIGZvciBzdW1zIG9mIGNvdW50cyBmcm9tIG5VTUkgdG8gblJlYWRzCmNvbG5hbWVzKHRpc3NAbWV0YS5kYXRhKVtjb2xuYW1lcyh0aXNzQG1ldGEuZGF0YSkgPT0gJ25VTUknXSA8LSAnblJlYWRzJwoKIyBDcmVhdGUgbWV0YWRhdGEgY29sdW1ucyBmb3IgY2VsbF9vbnRvbG9neV9jbGFzc3MgYW5kIHN1YmNlbGxfb250b2xvZ3lfY2xhc3NzCnRpc3NAbWV0YS5kYXRhWywnZnJlZV9hbm5vdGF0aW9uJ10gPC0gTkEKdGlzc0BtZXRhLmRhdGFbLCdjZWxsX29udG9sb2d5X2NsYXNzJ10gPC0gTkEKdGlzc0BtZXRhLmRhdGFbLCdzdWJjZWxsX29udG9sb2d5X2NsYXNzJ10gPC0gTkEKYGBgCgoKQ2FsY3VsYXRlIHBlcmNlbnQgcmlib3NvbWFsIGdlbmVzLgoKYGBge3J9CnJpYm8uZ2VuZXMgPC0gZ3JlcChwYXR0ZXJuID0gIl5ScFtzbF1bWzpkaWdpdDpdXSIsIHggPSByb3duYW1lcyh4ID0gdGlzc0BkYXRhKSwgdmFsdWUgPSBUUlVFKQpwZXJjZW50LnJpYm8gPC0gTWF0cml4Ojpjb2xTdW1zKHRpc3NAcmF3LmRhdGFbcmliby5nZW5lcywgXSkvTWF0cml4Ojpjb2xTdW1zKHRpc3NAcmF3LmRhdGEpCnRpc3MgPC0gQWRkTWV0YURhdGEob2JqZWN0ID0gdGlzcywgbWV0YWRhdGEgPSBwZXJjZW50LnJpYm8sIGNvbC5uYW1lID0gInBlcmNlbnQucmlibyIpCmBgYAoKQSBzYW5pdHkgY2hlY2s6IGdlbmVzIHBlciBjZWxsIHZzIHJlYWRzIHBlciBjZWxsLgoKYGBge3J9CkdlbmVQbG90KG9iamVjdCA9IHRpc3MsIGdlbmUxID0gIm5SZWFkcyIsIGdlbmUyID0gIm5HZW5lIiwgdXNlLnJhdz1UKQpgYGAKCkZpbHRlciBvdXQgY2VsbHMgd2l0aCBmZXcgcmVhZHMgYW5kIGZldyBnZW5lcy4KCmBgYHtyfQp0aXNzIDwtIEZpbHRlckNlbGxzKG9iamVjdCA9IHRpc3MsIHN1YnNldC5uYW1lcyA9IGMoIm5HZW5lIiwgIm5SZWFkcyIpLCAKICAgIGxvdy50aHJlc2hvbGRzID0gYyg1MDAsIDUwMDAwKSwgaGlnaC50aHJlc2hvbGRzID0gYygyNTAwMCwgMjAwMDAwMCkpCmBgYAoKCk5vcm1hbGl6ZSB0aGUgZGF0YSwgdGhlbiByZWdyZXNzIG91dCBjb3JyZWxhdGlvbiB3aXRoIHRvdGFsIHJlYWRzCmBgYHtyfQp0aXNzIDwtIE5vcm1hbGl6ZURhdGEob2JqZWN0ID0gdGlzcywgc2NhbGUuZmFjdG9yID0gMWU2KQp0aXNzIDwtIFNjYWxlRGF0YShvYmplY3QgPSB0aXNzKQp0aXNzIDwtIEZpbmRWYXJpYWJsZUdlbmVzKG9iamVjdCA9IHRpc3MsIGRvLnBsb3QgPSBUUlVFLCB4Lmxvdy5jdXRvZmYgPSAwLjcgLCB4LmhpZ2guY3V0b2ZmID0gSW5mLCB5LmN1dG9mZiA9IDAuNCkKYGBgCgoKUnVuIFByaW5jaXBhbCBDb21wb25lbnQgQW5hbHlzaXMuCmBgYHtyfQp0aXNzIDwtIFJ1blBDQShvYmplY3QgPSB0aXNzLCBkby5wcmludCA9IEZBTFNFKQp0aXNzIDwtIFByb2plY3RQQ0Eob2JqZWN0ID0gdGlzcywgZG8ucHJpbnQgPSBGQUxTRSkKYGBgCgpgYGB7ciwgZWNobz1GQUxTRSwgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9OH0KUENIZWF0bWFwKG9iamVjdCA9IHRpc3MsIHBjLnVzZSA9IDE6NywgY2VsbHMudXNlID0gNzAwLCBkby5iYWxhbmNlZCA9IFRSVUUsIGxhYmVsLmNvbHVtbnMgPSBGQUxTRSwgbnVtLmdlbmVzID0gOCkKYGBgCgpMYXRlciBvbiAoaW4gRmluZENsdXN0ZXJzIGFuZCBUU05FKSB5b3Ugd2lsbCBwaWNrIGEgbnVtYmVyIG9mIHByaW5jaXBhbCBjb21wb25lbnRzIHRvIHVzZS4gVGhpcyBoYXMgdGhlIGVmZmVjdCBvZiBrZWVwaW5nIHRoZSBtYWpvciBkaXJlY3Rpb25zIG9mIHZhcmlhdGlvbiBpbiB0aGUgZGF0YSBhbmQsIGlkZWFsbHksIHN1cHJlc3Npbmcgbm9pc2UuIFRoZXJlIGlzIG5vIGNvcnJlY3QgYW5zd2VyIHRvIHRoZSBudW1iZXIgdG8gdXNlLCBidXQgYSBkZWNlbnQgcnVsZSBvZiB0aHVtYiBpcyB0byBnbyB1bnRpbCB0aGUgcGxvdCBwbGF0ZWF1cy4KCmBgYHtyfQpQQ0VsYm93UGxvdChvYmplY3QgPSB0aXNzKQpgYGAKCkNob29zZSB0aGUgbnVtYmVyIG9mIHByaW5jaXBhbCBjb21wb25lbnRzIHRvIHVzZS4KYGBge3J9CiMgU2V0IG51bWJlciBvZiBwcmluY2lwYWwgY29tcG9uZW50cy4gCm4ucGNzID0gNwpgYGAKCgpUaGUgY2x1c3RlcmluZyBpcyBwZXJmb3JtZWQgYmFzZWQgb24gYSBuZWFyZXN0IG5laWdoYm9ycyBncmFwaC4gQ2VsbHMgdGhhdCBoYXZlIHNpbWlsYXIgZXhwcmVzc2lvbiB3aWxsIGJlIGpvaW5lZCB0b2dldGhlci4gVGhlIExvdXZhaW4gYWxnb3JpdGhtIGxvb2tzIGZvciBncm91cHMgb2YgY2VsbHMgd2l0aCBoaWdoIG1vZHVsYXJpdHktLW1vcmUgY29ubmVjdGlvbnMgd2l0aGluIHRoZSBncm91cCB0aGFuIGJldHdlZW4gZ3JvdXBzLiBUaGUgcmVzb2x1dGlvbiBwYXJhbWV0ZXIgZGV0ZXJtaW5lcyB0aGUgc2NhbGUuLi5oaWdoZXIgcmVzb2x1dGlvbiB3aWxsIGdpdmUgbW9yZSBjbHVzdGVycywgbG93ZXIgcmVzb2x1dGlvbiB3aWxsIGdpdmUgZmV3ZXIuCgpGb3IgdGhlIHRvcC1sZXZlbCBjbHVzdGVyaW5nLCBhaW0gdG8gdW5kZXItY2x1c3RlciBpbnN0ZWFkIG9mIG92ZXItY2x1c3Rlci4gSXQgd2lsbCBiZSBlYXN5IHRvIHN1YnNldCBncm91cHMgYW5kIGZ1cnRoZXIgYW5hbHl6ZSB0aGVtIGJlbG93LgoKYGBge3J9CiMgU2V0IHJlc29sdXRpb24gCnJlcy51c2VkIDwtIDIuNQoKdGlzcyA8LSBGaW5kQ2x1c3RlcnMob2JqZWN0ID0gdGlzcywgcmVkdWN0aW9uLnR5cGUgPSAicGNhIiwgZGltcy51c2UgPSAxOm4ucGNzLCAKICAgIHJlc29sdXRpb24gPSByZXMudXNlZCwgcHJpbnQub3V0cHV0ID0gMCwgc2F2ZS5TTk4gPSBUUlVFKQpgYGAKClRvIHZpc3VhbGl6ZSAKYGBge3J9CiMgSWYgY2VsbHMgYXJlIHRvbyBzcHJlYWQgb3V0LCB5b3UgY2FuIHJhaXNlIHRoZSBwZXJwbGV4aXR5LiBJZiB5b3UgaGF2ZSBmZXcgY2VsbHMsIHRyeSBhIGxvd2VyIHBlcnBsZXhpdHkgKGJ1dCBuZXZlciBsZXNzIHRoYW4gMTApLgp0aXNzIDwtIFJ1blRTTkUob2JqZWN0ID0gdGlzcywgZGltcy51c2UgPSAxOm4ucGNzLCBzZWVkLnVzZSA9IDEwLCBwZXJwbGV4aXR5PTIwKQpgYGAKCmBgYHtyfQojIG5vdGUgdGhhdCB5b3UgY2FuIHNldCBkby5sYWJlbD1UIHRvIGhlbHAgbGFiZWwgaW5kaXZpZHVhbCBjbHVzdGVycwpUU05FUGxvdChvYmplY3QgPSB0aXNzLCBkby5sYWJlbCA9IFQpCmBgYAoKQ2hlY2sgZXhwcmVzc2lvbiBvZiBnZW5lcyBvZiBpbnRlcnNldC4KCmBgYHtyLCBlY2hvPUZBTFNFLCBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9MTJ9CmdlbmVzX3RvX2NoZWNrID0gYygnQ2Q0JywgJ0NkNzlhJywgJ0NjcjInLCAnQ25uMycsICdDZDhhJywgJ0lsMnJiJywgJ0NkNScsICdDZDknLCAnVmNhbTEnKQojZ2VuZXNfdG9fY2hlY2sgPSBjKCdBbGInLCAnQ3lwMmYyJywgJ0N5cDJlMScsICdWZWdmJykKCkZlYXR1cmVQbG90KHRpc3MsIGdlbmVzX3RvX2NoZWNrLCBwdC5zaXplID0gMSwgbkNvbCA9IDMpCmBgYAoKRG90cGxvdHMgbGV0IHlvdSBzZWUgdGhlIGludGVuc2l0eSBvZiBleHBwcmVzc2lvbiBhbmQgdGhlIGZyYWN0aW9uIG9mIGNlbGxzIGV4cHJlc3NpbmcgZm9yIGVhY2ggb2YgeW91ciBnZW5lcyBvZiBpbnRlcmVzdC4KCmBgYHtyLCBlY2hvPUZBTFNFLCBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD04fQojIFRvIGNoYW5nZSB0aGUgeS1heGlzIHRvIHNob3cgcmF3IGNvdW50cywgYWRkIHVzZS5yYXcgPSBULgpEb3RQbG90KHRpc3MsIGdlbmVzX3RvX2NoZWNrLCBwbG90LmxlZ2VuZCA9IFQpCmBgYAoKSG93IGJpZyBhcmUgdGhlIGNsdXN0ZXJzPwpgYGB7cn0KdGFibGUodGlzc0BpZGVudCkKYGBgCgpXaGljaCBtYXJrZXJzIGlkZW50aWZ5IGEgc3BlY2lmaWMgY2x1c3Rlcj8KCmBgYHtyfQpjbHVzdC5tYXJrZXJzIDwtIEZpbmRNYXJrZXJzKG9iamVjdCA9IHRpc3MsIGlkZW50LjEgPSAzLCBpZGVudC4yID0gMSwgb25seS5wb3MgPSBUUlVFLCBtaW4ucGN0ID0gMC4yNSwgdGhyZXNoLnVzZSA9IDAuMjUpCmBgYAoKYGBge3J9CnByaW50KHggPSBoZWFkKHg9IGNsdXN0Lm1hcmtlcnMsIG4gPSAyMCwgYXZnX2RpZmYpKQpgYGAKCllvdSBjYW4gYWxzbyBjb21wdXRlIGFsbCBtYXJrZXJzIGZvciBhbGwgY2x1c3RlcnMgYXQgb25jZS4gVGhpcyBtYXkgdGFrZSBzb21lIHRpbWUuCmBgYHtyfQp0aXNzLm1hcmtlcnMgPC0gRmluZEFsbE1hcmtlcnMob2JqZWN0ID0gdGlzcywgb25seS5wb3MgPSBUUlVFLCBtaW4ucGN0ID0gMC4yNSwgdGhyZXNoLnVzZSA9IDAuMjUpCmBgYAoKRGlzcGxheSB0aGUgdG9wIG1hcmtlcnMgeW91IGNvbXB1dGVkIGFib3ZlLgpgYGB7cn0KdGlzcy5tYXJrZXJzICU+JSBncm91cF9ieShjbHVzdGVyKSAlPiUgdG9wX24oMTAsIGF2Z19sb2dGQykKYGBgCgojIyBBc3NpZ25pbmcgY2VsbCB0eXBlIGlkZW50aXR5IHRvIGNsdXN0ZXJzCgpBdCBhIGNvYXJzZSBsZXZlbCwgd2UgY2FuIHVzZSBjYW5vbmljYWwgbWFya2VycyB0byBtYXRjaCB0aGUgdW5iaWFzZWQgY2x1c3RlcmluZyB0byBrbm93biBjZWxsIHR5cGVzOgoKYGBge3J9CiMgc3Rhc2ggY3VycmVudCBjbHVzdGVyIElEcwp0aXNzIDwtIFN0YXNoSWRlbnQob2JqZWN0ID0gdGlzcywgc2F2ZS5uYW1lID0gImNsdXN0ZXIuaWRzIikKCiMgZW51bWVyYXRlIGN1cnJlbnQgY2x1c3RlciBJRHMgYW5kIHRoZSBsYWJlbHMgZm9yIHRoZW0KY2x1c3Rlci5pZHMgPC0gYygwLCAxLCAyLCAzLCA0LCA1LCA2LCA3LCA4LCA5LDEwLDExLDEyLDEzLDE0LDE1LDE2KQpjZWxsX29udG9sb2d5X2NsYXNzIDwtCiAgYygKICAiQiBjZWxsIiwKICAiQiBjZWxsIiwKICAiQiBjZWxsIiwKICAiVCBjZWxsIiwKICAiVCBjZWxsIiwKICAiQiBjZWxsIiwKICAiQiBjZWxsIiAsCiAgIkIgY2VsbCIsCiAgIkIgY2VsbCIsCiAgIkIgY2VsbCIsCiAgIkIgY2VsbCIsCiAgJ1QgY2VsbCcsCiAgJ0IgY2VsbCcsCiAgJ21hY3JvcGhhZ2UnLAogICdCIGNlbGwnLAogICdCIGNlbGwnLAogICdteWVsb2lkIGNlbGwnCiAgKQoKdmFsaWRhdGVfY2VsbF9vbnRvbG9neShjZWxsX29udG9sb2d5X2NsYXNzKQpjZWxsX29udG9sb2d5X2lkID0gY29udmVydF90b19jZWxsX29udG9sb2d5X2lkKGNlbGxfb250b2xvZ3lfY2xhc3MpCgp0aXNzQG1ldGEuZGF0YVsnZnJlZV9hbm5vdGF0aW9uJ10gPC0gYXMuY2hhcmFjdGVyKHBseXI6Om1hcHZhbHVlcyh4ID0gdGlzc0BpZGVudCwgZnJvbSA9IGNsdXN0ZXIuaWRzLCB0byA9IGZyZWVfYW5ub3RhdGlvbikpCnRpc3NAbWV0YS5kYXRhWydjZWxsX29udG9sb2d5X2NsYXNzJ10gPC0gYXMuY2hhcmFjdGVyKHBseXI6Om1hcHZhbHVlcyh4ID0gdGlzc0BpZGVudCwgZnJvbSA9IGNsdXN0ZXIuaWRzLCB0byA9IGNlbGxfb250b2xvZ3lfY2xhc3MpKQp0aXNzQG1ldGEuZGF0YVsnY2VsbF9vbnRvbG9neV9pZCddIDwtIGFzLmNoYXJhY3RlcihwbHlyOjptYXB2YWx1ZXMoeCA9IHRpc3NAaWRlbnQsIGZyb20gPSBjbHVzdGVyLmlkcywgdG8gPSBjZWxsX29udG9sb2d5X2lkKSkKCgpUU05FUGxvdChvYmplY3QgPSB0aXNzLCBkby5sYWJlbCA9IFRSVUUsIHB0LnNpemUgPSAwLjUsIGdyb3VwLmJ5PSdjZWxsX29udG9sb2d5X2NsYXNzJykKYGBgCgoKIyMgQ2hlY2tpbmcgZm9yIGJhdGNoIGVmZmVjdHMKCgpDb2xvciBieSBtZXRhZGF0YSwgbGlrZSBwbGF0ZSBiYXJjb2RlLCB0byBjaGVjayBmb3IgYmF0Y2ggZWZmZWN0cy4KYGBge3J9ClRTTkVQbG90KG9iamVjdCA9IHRpc3MsIGRvLnJldHVybiA9IFRSVUUsIGdyb3VwLmJ5ID0gInBsYXRlLmJhcmNvZGUiKQpgYGAKClByaW50IGEgdGFibGUgc2hvd2luZyB0aGUgY291bnQgb2YgY2VsbHMgaW4gZWFjaCBpZGVudGl0eSBjYXRlZ29yeSBmcm9tIGVhY2ggcGxhdGUuCgpgYGB7cn0KdGFibGUoYXMuY2hhcmFjdGVyKHRpc3NAaWRlbnQpLCBhcy5jaGFyYWN0ZXIodGlzc0BtZXRhLmRhdGEkcGxhdGUuYmFyY29kZSkpCmBgYAoKCiMgU2F2ZSB0aGUgUm9iamVjdCBmb3IgbGF0ZXIKV2hlbiB5b3Ugc2F2ZSB0aGUgYW5ub3RhdGVkIHRpc3N1ZSwgcGxlYXNlIGdpdmUgaXQgYSBuYW1lLgoKYGBge3J9CmZpbGVuYW1lID0gaGVyZSgnMDBfZGF0YV9pbmdlc3QnLCAnMDRfdGlzc3VlX3JvYmpfZ2VuZXJhdGVkJywgCiAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCgiZmFjcyIsIHRpc3N1ZV9vZl9pbnRlcmVzdCwgIl9zZXVyYXRfdGlzcy5Sb2JqIikpCnByaW50KGZpbGVuYW1lKQpzYXZlKHRpc3MsIGZpbGU9ZmlsZW5hbWUpCmBgYAoKYGBge3J9CiMgVG8gcmVsb2FkIGEgc2F2ZWQgb2JqZWN0CiMgZmlsZW5hbWUgPSBoZXJlKCcwMF9kYXRhX2luZ2VzdCcsICcwNF90aXNzdWVfcm9ial9nZW5lcmF0ZWQnLCAKIyAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoImZhY3MiLCB0aXNzdWVfb2ZfaW50ZXJlc3QsICJfc2V1cmF0X3Rpc3MuUm9iaiIpKQojIGxvYWQoZmlsZT1maWxlbmFtZSkKYGBgCgoKCiMgRXhwb3J0IHRoZSBmaW5hbCBtZXRhZGF0YQoKU28gdGhhdCBCaW9odWIgY2FuIGVhc2lseSBjb21iaW5lIGFsbCB5b3VyIGNlbGxfb250b2xvZ3lfY2xhc3NzLCBwbGVhc2UgZXhwb3J0IHRoZW0gYXMgYSBzaW1wbGUgY3N2LgoKYGBge3J9CmhlYWQodGlzc0BtZXRhLmRhdGEpCmBgYAoKCmBgYHtyfQpmaWxlbmFtZSA9IGhlcmUoJzAwX2RhdGFfaW5nZXN0JywgJzAzX3Rpc3N1ZV9hbm5vdGF0aW9uX2NzdicsIAogICAgICAgICAgICAgICAgICAgICBwYXN0ZTAodGlzc3VlX29mX2ludGVyZXN0LCAiX2ZhY3NfYW5ub3RhdGlvbi5jc3YiKSkKd3JpdGUuY3N2KEZldGNoRGF0YSh0aXNzLCBjKCdwbGF0ZS5iYXJjb2RlJywnY2VsbF9vbnRvbG9neV9jbGFzcycsJ2NlbGxfb250b2xvZ3lfaWQnLCAnZnJlZV9hbm5vdGF0aW9uJywgJ3RTTkVfMScsICd0U05FXzInKSksIGZpbGU9ZmlsZW5hbWUpCmBgYAoKCgo=